﻿//Copyright (c) 2010 Christopher E. S. King (http://chriskingconsulting.com)
//
//Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files 
// (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, 
// publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do 
// so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE 
// FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.

using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Xml.Linq;
using System.Reflection;
using System.Collections;
using System.IO;
using System.Diagnostics.Contracts;

using King.Extensions;
using King.Text;
using King.Reflection;
using King.Language;
using King.Language.CSharp;
using King.Antlr3;

using Antlr.Runtime;
using Antlr.Runtime.Tree;

namespace King.Language.CSharp {

    public static class CSharpParser {

        public unsafe static IEnumerable<CSharpToken> Parse(TextReader code, IEnumerable<string> preProcessorSymbols = null) {

            // activate parser
            var stream = new ANTLRReaderStream(code);
            var lexer = new AntlrCSharpLexer(stream);
            var tokens = new MyCommonTokenStream(lexer);
            var parser = new AntlrCSharpParser(tokens);

            // pre-processor symbols
            lexer.AddPreProcessorSymbols(preProcessorSymbols);

            // allocator
            int cBuffer = 256 * 256;
            byte* pBuffer = stackalloc byte[cBuffer];
            var allocator = new Allocator(pBuffer, cBuffer);

            // adapter
            var adapter = new TokenTreeAdaptor(allocator, tokenNames: parser.TokenNames, stream: stream);

            // expose adapter to create tokens/trees
            parser.TreeAdaptor = adapter;
            lexer.TokenTreeAdapter = adapter;

            // parse code
            var scope = (IAstRuleReturnScope)typeof(AntlrCSharpParser).Invoke("start", parser);
            var tree = (NillableToken)scope.Tree;

            // convert antlr internal tokens (NillableToken) to antlr public tokens (AntlrToken) to csharp tokens (CSharpToken)
            if (tree.IsNil)
                return tree.Children().Select(o => new CSharpToken(adapter, o.AntlrToken)).ToArray();
                
            return new CSharpToken[] { new CSharpToken(adapter, tree.AntlrToken) };
        }
        public static IEnumerable<CSharpToken> Parse(string code, IEnumerable<string> preProcessorSymbols = null) {
            return Parse(new StringReader(code), preProcessorSymbols);
        }
        public static IEnumerable<CSharpToken> Parse(Stream stream, IEnumerable<string> preProcessorSymbols = null) {
            return Parse(new StreamReader(stream), preProcessorSymbols);
        }
        public static IEnumerable<CSharpToken> ParseFile(string file, IEnumerable<string> preProcessorSymbols = null) {
            return Parse(new StreamReader(file), preProcessorSymbols);
        }
    }

    [DebuggerTypeProxy(typeof(DebuggerTypeProxy))]
    public struct CSharpToken {

        private class DebuggerTypeProxy {

            private readonly CSharpToken m_token;

            public DebuggerTypeProxy(CSharpToken token) {
                m_token = token;
            }

            [DebuggerBrowsable(DebuggerBrowsableState.RootHidden)]
            public CSharpToken[] Children {
                get { return m_token.Children().ToArray(); }
            }
        }

        private AntlrToken m_token;
        private TokenTreeAdaptor m_adaptor;

        internal CSharpToken(TokenTreeAdaptor adaptor, AntlrToken token) {
            m_token = token;
            m_adaptor = adaptor;
        }

        internal bool IsDefault {
            get { return m_token.IsDefault; }
        }

        public int Type {
            get { return m_token.Type; }
        }

        public int Line {
            get { return m_token.Line; }
        }
        public int Column {
            get { return m_token.Column; }
        }
        public int Position {
            get { return m_token.Position; }
        }
        public int Length {
            get { return m_token.Length; }
        }
        public string Text {
            get { throw new NotImplementedException(); }
        }

        public CSharpToken StartToken {
            get { return new CSharpToken(m_adaptor, m_token.StartToken); }
        }
        public CSharpToken EndToken {
            get { return new CSharpToken(m_adaptor, m_token.EndToken); }
        }
        public IEnumerable<CSharpToken> Children() {
            var adaptor = m_adaptor;
            return m_token.Children(m_adaptor).Select(o => new CSharpToken(adaptor, o));
        }

        public override string ToString() {
            return AntlrCSharpParser.tokenNames[Type];
        }
    }

    public abstract class CSharpAst : Ast {

        internal CSharpAst(IEnumerable<Ast> children = null, NillableToken? token = null)
            : base(children, token) {
        }
    }
    public sealed class CSharpNamespace : CSharpAst {

        internal CSharpNamespace(NillableToken token, IEnumerable<CSharpAst> children)
            : base(children) {
        }
    }
    public sealed class CSharpUsing : CSharpAst {

        internal CSharpUsing(NillableToken token, IEnumerable<CSharpAst> children)
            : base(children) {
        }
    }
    public sealed class CSharpName : CSharpAst {

        internal CSharpName(NillableToken token, IEnumerable<CSharpAst> children = null)
            : base(children) {
        }
    }
    public sealed class CSharpIdentifier : CSharpAst {

        internal CSharpIdentifier(NillableToken token, IEnumerable<CSharpAst> children = null)
            : base(children) {
        }
    }
}
